/*
 * Copyright 2018-2021 guerlab.net and other contributors.
 *
 * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE, Version 3 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.guerlab.spring.web.properties;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.http.HttpMethod;

import java.util.ArrayList;
import java.util.List;

/**
 * 全局异常处理配置
 *
 * @author guer
 */
@RefreshScope
@ConfigurationProperties("spring.global-exception")
public class GlobalExceptionProperties {

    /**
     * 通用匹配符
     */
    private static final String ALL = "*";

    /**
     * 是否打印堆栈根据
     */
    private boolean printStackTrace;

    /**
     * 堆栈跟踪排除列表
     */
    private List<String> stackTraceExcludes = new ArrayList<>();

    /**
     * 堆栈跟踪包含列表
     */
    private List<String> stackTraceIncludes = new ArrayList<>();

    /**
     * 日志忽略路径列表
     */
    private List<Url> logIgnorePaths = new ArrayList<>();

    /**
     * 获取是否打印堆栈根据
     *
     * @return 是否打印堆栈根据
     */
    public boolean isPrintStackTrace() {
        return printStackTrace;
    }

    /**
     * 设置是否打印堆栈根据
     *
     * @param printStackTrace
     *         是否打印堆栈根据
     */
    public void setPrintStackTrace(boolean printStackTrace) {
        this.printStackTrace = printStackTrace;
    }

    /**
     * 判断是否在排除列表中匹配
     *
     * @param methodKey
     *         方法签名
     * @return 是否匹配
     */
    public boolean excludeMatch(String methodKey) {
        return match(stackTraceExcludes, methodKey);
    }

    /**
     * 判断是否在包含列表中匹配
     *
     * @param methodKey
     *         方法签名
     * @return 是否匹配
     */
    public boolean includeMatch(String methodKey) {
        return match(stackTraceIncludes, methodKey);
    }

    /**
     * 判断是否在列表中匹配
     *
     * @param list
     *         列表
     * @param methodKey
     *         方法签名
     * @return 是否匹配
     */
    private boolean match(List<String> list, String methodKey) {
        return list.contains(ALL) || list.parallelStream().anyMatch(methodKey::startsWith);
    }

    /**
     * 获取堆栈跟踪排除列表
     *
     * @return 堆栈跟踪排除列表
     */
    public List<String> getStackTraceExcludes() {
        return stackTraceExcludes;
    }

    /**
     * 设置堆栈跟踪排除列表
     *
     * @param stackTraceExcludes
     *         堆栈跟踪排除列表
     */
    public void setStackTraceExcludes(List<String> stackTraceExcludes) {
        this.stackTraceExcludes = stackTraceExcludes;
    }

    /**
     * 获取堆栈跟踪包含列表
     *
     * @return 堆栈跟踪包含列表
     */
    public List<String> getStackTraceIncludes() {
        return stackTraceIncludes;
    }

    /**
     * 设置堆栈跟踪包含列表
     *
     * @param stackTraceIncludes
     *         堆栈跟踪包含列表
     */
    public void setStackTraceIncludes(List<String> stackTraceIncludes) {
        this.stackTraceIncludes = stackTraceIncludes;
    }

    /**
     * 获取日志忽略路径列表
     *
     * @return 日志忽略路径列表
     */
    public List<Url> getLogIgnorePaths() {
        return logIgnorePaths;
    }

    /**
     * 设置日志忽略路径列表
     *
     * @param logIgnorePaths
     *         日志忽略路径列表
     */
    public void setLogIgnorePaths(List<Url> logIgnorePaths) {
        this.logIgnorePaths = logIgnorePaths;
    }

    /**
     * 忽略路径
     */
    public static class Url {

        /**
         * 请求方式
         */
        private HttpMethod method;

        /**
         * 路径
         */
        private String path;

        /**
         * 获取请求方式
         *
         * @return 请求方式
         */
        public HttpMethod getMethod() {
            return method;
        }

        /**
         * 设置请求方式
         *
         * @param method
         *         请求方式
         */
        public void setMethod(HttpMethod method) {
            this.method = method;
        }

        /**
         * 获取路径
         *
         * @return 路径
         */
        public String getPath() {
            return path;
        }

        /**
         * 设置路径
         *
         * @param path
         *         路径
         */
        public void setPath(String path) {
            this.path = path;
        }
    }
}
